﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Xml;
using AutomationHelper;
using System.Reflection;
using HPAGMRestAPIWrapper;
using System.Windows;
using System.Windows.Threading;
using System.Threading;
using System.IO;
using System.Xml.Serialization;
using Hardcodet.Wpf.TaskbarNotification;

namespace AGMLite.BaseClass
{
    public class AGMProjects
    {
        public ObservableCollectionEx<AGMProject> ProjectCollection { get; set; }

        public AGMProjects() { }

        public void Load()
        {
            ProjectCollection = new ObservableCollectionEx<AGMProject>();

            try
            {
                if (!System.IO.Directory.Exists(Common.ApplicationSetting.SettingStoreFolder))
                    System.IO.Directory.CreateDirectory(Common.ApplicationSetting.SettingStoreFolder);

                string configFile = System.IO.Path.Combine(Common.ApplicationSetting.SettingStoreFolder, "settings.xml");
                if (!System.IO.File.Exists(configFile))
                    return;

                XmlDocument doc = new XmlDocument();
                doc.Load(configFile);

                foreach (XmlNode projNode in doc.SelectNodes("Monitor/Projects/Project"))
                {
                    AGMProject newProject = new AGMProject()
                    {
                        GUID = projNode.Attributes["guid"].Value,
                        Name = projNode.Attributes["name"].Value,
                        Icon = projNode.Attributes["icon"] == null ? string.Empty : projNode.Attributes["icon"].Value,
                        Server = projNode.Attributes["server"].Value,
                        Username = projNode.Attributes["username"].Value,
                        Password = Encryption.Decrypt(projNode.Attributes["password"].Value, projNode.Attributes["username"].Value.Substring(0,8)),
                        Domain = projNode.Attributes["domain"].Value,
                        Project = projNode.Attributes["project"].Value,
                        Status = 0,
                        Message = "Disconnected",
                    };

                    #region Get Items for project
                    ObservableCollectionEx<AGMItem> monitorItems = new ObservableCollectionEx<AGMItem>();

                    foreach (XmlNode itemNode in projNode.SelectNodes("Items/Item"))
                    {
                        ObservableCollectionEx<AGMLiteEntityFilterField> itemFilterFields = new ObservableCollectionEx<AGMLiteEntityFilterField>();
                        ObservableCollectionEx<AGMLiteEntityViewField> itemViewFields = new ObservableCollectionEx<AGMLiteEntityViewField>();

                        #region Get filter fields
                        foreach (XmlNode filterFieldNode in itemNode.SelectNodes("Filters/Field"))
                        {
                            AGMLiteEntityFilterField newFilterField = new AGMLiteEntityFilterField()
                            {
                                Operation = GetOperation(filterFieldNode.Attributes["operation"].Value),
                                Field = new AGMField()
                                {
                                    Name = filterFieldNode.Attributes["name"].Value,
                                    Value = filterFieldNode.Attributes["value"].Value,
                                    Entity = filterFieldNode.Attributes["fieldtype"].Value,
                                }
                            };
                            itemFilterFields.Add(newFilterField);
                        }
                        #endregion

                        #region Get View fields
                        foreach (XmlNode viewFieldNode in itemNode.SelectNodes("Views/Field"))
                        {
                            AGMLiteEntityViewField newViewField = new AGMLiteEntityViewField()
                            {
                                Order = Convert.ToInt32(viewFieldNode.Attributes["order"].Value),
                                Field = new AGMField()
                                {
                                    Name = viewFieldNode.Attributes["name"].Value,
                                    Entity = viewFieldNode.Attributes["fieldtype"].Value,
                                }
                            };
                            itemViewFields.Add(newViewField);
                        }
                        #endregion

                        AGMItem newItem = new AGMItem()
                        {
                            GUID = itemNode.Attributes["guid"].Value,
                            Name = itemNode.Attributes["name"].Value,
                            EntityType = GetEntityType(itemNode.Attributes["entitytype"].Value),
                            SyncInterval = Convert.ToInt32(itemNode.Attributes["syncinterval"].Value),                            
                            CanEditEntityType = true,
                            IsRequireSync = false,
                            Status = Convert.ToBoolean(itemNode.Attributes["isenabled"].Value) ? 0 : 4,
                            Total = 0,
                            LastStatus = itemNode.Attributes["laststatus"] == null ? 0 :Convert.ToInt32(itemNode.Attributes["laststatus"].Value),
                            IsEnabled = Convert.ToBoolean(itemNode.Attributes["isenabled"].Value),
                            FilterFieldCollection = itemFilterFields,
                            ViewFieldCollection = itemViewFields,
                            Project = newProject,
                        };
                        monitorItems.Add(newItem);
                    }
                    #endregion

                    newProject.ItemCollection = monitorItems;

                    ProjectCollection.Add(newProject);
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public void DisconnectAll()
        {
            System.Threading.Tasks.Parallel.ForEach<AGMProject>(ProjectCollection, project =>
            {
                if (project.Connection != null)
                    project.Connection.Logoff();
            });
        }

        #region Private methods
        private Common.AGMLiteEnum.EntityType GetEntityType(string value)
        {
            if (value == null)
                throw new Exception("Entity type is empty.");

            return (Common.AGMLiteEnum.EntityType)Enum.Parse(typeof(Common.AGMLiteEnum.EntityType), value, true);
        }

        private Common.AGMLiteEnum.Operation GetOperation(string value)
        {
            if (value == null)
                throw new Exception("Operation is empty.");

            return (Common.AGMLiteEnum.Operation)Enum.Parse(typeof(Common.AGMLiteEnum.Operation), value, true);
        }
        #endregion
    }

    public class AGMProject : INotifyPropertyChanged, IDataErrorInfo 
    {
        public event PropertyChangedEventHandler PropertyChanged;

        #region Private members
        private double _interval = TimeSpan.FromMinutes(30).TotalMilliseconds;
        private string _name;
        private string _icon;
        private string _server;
        private string _username;
        private string _password;
        private string _domain;
        private string _project;
        private int _status;
        private string _message;
        private ObservableCollectionEx<AGMItem> _ItemCollection;
        private Dictionary<string, string> _errorcollection;

        private BackgroundWorker _connectWorker;
        #endregion

        #region Properties
        public TaskbarIcon TaskBarIcon { get; set; }
        public string GUID { get; set; }
        public Dictionary<string, List<AGMField>> EntityFields { get; set; }
        public AGMConnection Connection { get; set; }
        public System.Timers.Timer ProjectTimer { get; set; }
        public bool IsEditing { get; set; }

        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                if (value != _name)
                {
                    _name = value;
                    OnPropertyChanged("Name");
                }
            }
        }

        public string Server
        {
            get
            {
                return _server;
            }
            set
            {
                if (value != _server)
                {
                    _server = value;
                    OnPropertyChanged("Server");
                }
            }
        }

        public string Username
        {
            get
            {
                return _username;
            }
            set
            {
                if (value != _username)
                {
                    _username = value;
                    OnPropertyChanged("Username");
                }
            }
        }

        public string Password
        {
            get
            {
                return _password;
            }
            set
            {
                if (value != _password)
                {
                    _password = value;
                    OnPropertyChanged("Password");
                }
            }
        }

        public string Domain
        {
            get
            {
                return _domain;
            }
            set
            {
                if (value != _domain)
                {
                    _domain = value;
                    OnPropertyChanged("Domain");
                }
            }
        }

        public string Project
        {
            get
            {
                return _project;
            }
            set
            {
                if (value != _project)
                {
                    _project = value;
                    OnPropertyChanged("Project");
                }
            }
        }

        public string Icon
        {
            get
            {
                return _icon;
            }
            set
            {
                if (value != _icon)
                {
                    _icon = value;
                    OnPropertyChanged("Icon");
                }
            }
        }       

        public ObservableCollectionEx<AGMItem> ItemCollection
        {
            get
            {
                return _ItemCollection;
            }
            set
            {
                if (value != _ItemCollection)
                {
                    _ItemCollection = value;
                    OnPropertyChanged("ItemCollection");
                }
            }
        }

        /// <summary>
        /// Project status, 0 - disconnected, 1 - connecting, 2 - connected
        /// </summary>
        public int Status
        {
            get
            {
                return _status;
            }
            set
            {
                if (value != _status)
                {
                    _status = value;
                    OnPropertyChanged("Status");
                }
            }
        }

        public string Message
        {
            get
            {
                return _message;
            }
            set
            {
                if (value != _message)
                {
                    _message = value;
                    OnPropertyChanged("Message");
                }
            }
        }       
        #endregion

        #region Constructor
        public AGMProject()
        {
            ItemCollection = new ObservableCollectionEx<AGMItem>();
            EntityFields = new Dictionary<string, List<AGMField>>();
            _errorcollection = new Dictionary<string, string>();
            InitialTimer();
        }
        #endregion        

        public AGMProject ShallowCopy()
        {
            AGMProject clonedProject = (AGMProject)this.MemberwiseClone();
            return clonedProject;
        }

        #region Error Info
        public string Error
        {
            get 
            {
                if (_errorcollection.Count == 0)
                    return null;

                StringBuilder errorList = new StringBuilder();
                var errorMessages = _errorcollection.Values.GetEnumerator();
                while (errorMessages.MoveNext())
                    errorList.AppendLine(errorMessages.Current);

                return errorList.ToString();
            }
        }

        public string this[string propertyName]
        {
            get 
            {
                string msg = null;
                switch(propertyName)
                {
                    case "Name":
                        if (string.IsNullOrEmpty(this.Name))
                            msg = "Project name cannot be empty";
                        break;
                    case "Server":
                        if (string.IsNullOrEmpty(this.Server))
                            msg = "Server cannot be empty";
                        else if (!this.Server.StartsWith("http") || !this.Server.StartsWith("https"))
                            msg = "Server must start with http or https";
                        break;
                    case "Username":
                        if (string.IsNullOrEmpty(this.Username))
                            msg = "Username cannot be empty";
                        break;
                    case "Password":
                        if (string.IsNullOrEmpty(this.Password))
                            msg = "Password cannot be empty";
                        break;
                    case "Domain":
                        if (string.IsNullOrEmpty(this.Domain))
                            msg = "Domain cannot be empty";
                        break;
                    case "Project":
                        if (string.IsNullOrEmpty(this.Project))
                            msg = "Project cannot be empty";
                        break;
                }

                if (msg != null && !_errorcollection.ContainsKey(propertyName))
                    _errorcollection.Add(propertyName, msg);
                if (msg == null && _errorcollection.ContainsKey(propertyName))
                    _errorcollection.Remove(propertyName);

                return msg;
            }
        }
        #endregion

        #region Public methods
        public void Connect()
        {
            _connectWorker = new BackgroundWorker();
            _connectWorker.DoWork += new DoWorkEventHandler(ConnectWorker_DoWork);
            _connectWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(ConnectWorker_RunWorkerCompleted);
            _connectWorker.RunWorkerAsync();            
        }

        public void StartTimer()
        {
            ProjectTimer.Start();
        }

        public void ResetTimer()
        {
            ProjectTimer.Stop();
            ProjectTimer.Start();
        }

        public void Save(bool isDelete)
        {
            XmlNode projectsNode;

            try
            {
                string configFile = System.IO.Path.Combine(Common.ApplicationSetting.SettingStoreFolder, "settings.xml");

                // Configuration file is not existed, create it
                if (!System.IO.File.Exists(configFile))
                {
                    XmlDocument doc = XMLHelper.CreateXmlDocument("Monitor");
                    XmlNode rootNode = doc.LastChild;

                    projectsNode = XMLHelper.CreateChildNode(rootNode, "Projects");

                    doc.Save(configFile);
                }

                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(configFile);
                projectsNode = xmlDoc.SelectSingleNode("Monitor/Projects");

                XmlNode projectNode = projectsNode.SelectSingleNode(string.Format("Project[@guid='{0}']", GUID));
                if (projectNode == null) // it is a new project, to add
                {
                    XmlNode addProjectNode = XMLHelper.CreateChildNode(projectsNode, "Project");
                    XMLHelper.CreateAttribute(addProjectNode, "guid", GUID);
                    XMLHelper.CreateAttribute(addProjectNode, "name", Name);
                    XMLHelper.CreateAttribute(addProjectNode, "server", Server);
                    XMLHelper.CreateAttribute(addProjectNode, "username", Username);
                    XMLHelper.CreateAttribute(addProjectNode, "password", Encryption.Encrypt(Password, Username.Substring(0,8)));
                    XMLHelper.CreateAttribute(addProjectNode, "domain", Domain);
                    XMLHelper.CreateAttribute(addProjectNode, "project", Project);
                    XMLHelper.CreateAttribute(addProjectNode, "icon", Icon);
                }
                else
                {
                    if (!isDelete) // edit some field's value
                    {
                        projectNode.Attributes["name"].Value = Name;
                        projectNode.Attributes["server"].Value = Server;
                        projectNode.Attributes["username"].Value = Username;
                        projectNode.Attributes["password"].Value = Encryption.Encrypt(Password, Username.Substring(0, 8));
                        projectNode.Attributes["domain"].Value = Domain;
                        projectNode.Attributes["project"].Value = Project;
                        projectNode.Attributes["icon"].Value = Icon;
                    }
                    else // delete project
                    {
                        projectsNode.RemoveChild(projectNode);
                    }
                }
                
                xmlDoc.Save(configFile);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        
        #endregion

        #region Private methods
        private void InitialTimer()
        {
            ProjectTimer = new System.Timers.Timer();
            ProjectTimer.Interval = _interval; 
            ProjectTimer.AutoReset = true;
            ProjectTimer.Elapsed += new System.Timers.ElapsedEventHandler(ProjectTimer_Elapsed);
        }

        private void ProjectTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (!IsEditing)
                Connect();
        }

        private void AddEntityFieldDictionary(string entityType)
        {
            if (EntityFields.ContainsKey(entityType))
                EntityFields[entityType] = Common.AGMRestAPIHelper.GetEntityFields(Connection, entityType);
            else
                EntityFields.Add(entityType, Common.AGMRestAPIHelper.GetEntityFields(Connection, entityType));
        }

        private void ConnectWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                System.Threading.Thread.Sleep(2000);
                Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                {
                    Message = "Connecting...";
                    Status = 1; // Connecting
                });
                Connection = new AGMConnection(Server, Username, Password, Domain, Project);
                AddEntityFieldDictionary(Common.AGMLiteEnum.GetEntityTypeFromEnum(Common.AGMLiteEnum.EntityType.Requirement));
                AddEntityFieldDictionary(Common.AGMLiteEnum.GetEntityTypeFromEnum(Common.AGMLiteEnum.EntityType.Defect));
                AddEntityFieldDictionary(Common.AGMLiteEnum.GetEntityTypeFromEnum(Common.AGMLiteEnum.EntityType.ProjectTask));
                AddEntityFieldDictionary("release-backlog-item");
                //foreach (AGMItem item in ItemCollection)
                //{
                //    item.UpdateAllFields();
                //}
            }
            catch (Exception ex)
            {
                Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
                {
                    Status = 0;
                    Message = string.Format("Disconnected. {0}", ex.Message);
                });                
            }
        }        
        
        private void ConnectWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            _connectWorker.Dispose();
            _connectWorker = null;

            if (Connection != null)
            {                
                Status = 2; // connected
                Message = "Connected";
                foreach (AGMItem item in ItemCollection)
                {
                    item.TaskBarIcon = TaskBarIcon;
                    item.Project = this;
                    item.Sync();
                    System.Threading.Thread.Sleep(100);                    
                }
            }
        }

        #region INotifyPropertyChanged Members
        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion
        #endregion

    }
}
